divides one 16-bit value into another 16-bit value to compute a 16-bit result and remainder.
PBASIC allows you to divide any two variables--bits, bytes, words, or a mixture--to get a 16-bit result or remainder.
This routine duplicates that capability in assembly language. It uses a binary interpretation of the long-division method you were probably taught in school. It shifts the divisor left until there is a 1 in the msb. It then attempts to subtract this number from the dividend.
If the result of the subtraction is positive, the routine puts a 1 into the lsb of the quotient, shifts the quotient left, shifts the divisor right, and repeats the operation on what remains of the dividend. If the result of the subtraction is negative, the routine undoes the subtraction by adding the divisor and dividend back together, puts a 0 into the lsb of the quotient, shifts the quotient left, shifts the divisor right, and repeats the operation.
It may require a walk through the routine with paper and pencil (or the simulator) to grasp how this works. When the routine finishes, the 16-bit variable consisting of qH and qL holds the quotient or result of the division. The variable consisting of topH and topL holds the remainder.
Division by 0 is illegal, so the routine starts by checking for this. If division by 0 is attempted, the routine aborts and returns the error code 0FFh (255) in the w register. Otherwise, it returns with 0 in w. Your program should either prevent division by 0 or be prepared to act on the error code.
To see Divide in operation, either run the program with the PSIM simulator,
or connect the circuit below to an erasable PIC or PIC emulator, such as
the Parallax downloader. Assemble and run DIVIDE.SRC. When you apply power
to the PIC, the LEDs will light up in the binary pattern of the answer to
the math problem 330Ah/00A3h, which is 50h with a remainder of 1Ah. So the
quotient (50h) displays the following binary pattern on the LEDs:
0000 0000 0101 0000
Try dividing various values to see different results. Also, modify the program to display remainders (stored in the variables topH and topL) on the LEDs.
; ; *************************************************************************** ; *** Bubble Software Parallax to PIC Source Converter. Copyright 1999. *** ; *** http://www.bubblesoftonline.com email: sales@picnpoke.com *** ; *************************************************************************** ; ; DIVIDE ; Divide one 16-bit number into another, returning the 16-bit result and ; the remainder. Upon entry, the top of the division fraction (the dividend) ; must be in topH and topL, and the bottom (divisor) in btmH and btmL. ; If division by zero is attempted, the routine will return immediately with ; the error code of 0FFh in w. Otherwise, it will perform the division, leaving ; the remainder in topH and topL and the result (quotient) in qH and qL. ; Upon return from a successful division, w contains 0. ; Device data and reset vector P = pic16c55 #include <16c55.inc> ; processor assembler definitions _CONFIG _xt_osc & _wdt_off & _protect_off reset start org 8 topH Res d'1' ; MSB of top of fraction. topL Res d'1' ; LSB of top of fraction. btmH Res d'1' ; MSB of bottom of fraction. btmL Res d'1' ; LSB of bottom of fraction. qH Res d'1' ; MSB of quotient. qL Res d'1' ; LSB of quotient. count Res d'1' ; temporary counter index Res d'1' ; temporary counter org 0 start MOVLW d'0' ; Set to outputs to display TRIS 6h MOVLW d'0' ; results on LEDs. TRIS 7h MOVLW 0x33 ; Problem: divide MOVWF topH MOVLW 0x0A ; 330Ah by 00A3h. MOVWF topL MOVLW 0x00 MOVWF btmH MOVLW 0x00A3 MOVWF btmL CALL divide MOVF qL,w ; Display quotient in binary MOVWF 6h MOVF qH,w ; on LEDs connected to rb and rc. MOVWF 7h GOTO $ ; Endless loop Divide MOVF btmH,w ; Check for division by 0. IORWF btmL,w BTFSC status,z RETLW d'255' ; Error code= 255: return. MOVLW d'1' ; Otherwise, initialize variables MOVWF count CLRF index ; for the division. CLRF qH CLRF qL Divide_sh_loop BTFSC btmH,d'7' ; Shift divisor left GOTO Divide_d1 BCF status,c ; until msb is in RLF btmL ; btmH.7. RLF btmH ; count = no. of shifts+1. INCF count GOTO Divide_sh_loop Divide_d1 BCF status,c RLF qL ; Shift quotient left. RLF qH MOVF btmL,w ; top = top - btm. SUBWF topL BTFSC status,c ; If top - btm < 0 then GOTO Divide_d2 MOVLW d'1' ; top = top + btm SUBWF topH BTFSC status,c ; The idea is to do the GOTO Divide_d2 INCF topH ; the subtraction and comparison MOVF btmL,w ; (top > btm?) in one step. ADDWF topL goto Divide_reentr ; Then, if btm > top, undo <Microchip instruction> Divide_d2 MOVF btmH,w ; the subtraction by adding SUBWF topH BTFSS status,c ; top and btm back together goto Divide_less ; <Microchip instruction> BSF qL,d'0' Divide_reentr BCF status,c RRF btmH RRF btmL DECFSZ count GOTO Divide_d1 RETLW 0h ; Return w/ remainder in top ; and result in q. Divide_less MOVF btmL,w ; btm > top, so ADDWF topL BTFSC status,c ; undo the subtraction by INCF topH ; adding them back together. MOVF btmH,w ADDWF topH goto Divide_reentr ; <Microchip instruction> end
; DIVIDE ; Divide one 16-bit number into another, returning the 16-bit result and ; the remainder. Upon entry, the top of the division fraction (the dividend) ; must be in topH and topL, and the bottom (divisor) in btmH and btmL. ; If division by zero is attempted, the routine will return immediately with ; the error code of 0FFh in w. Otherwise, it will perform the division, leaving ; the remainder in topH and topL and the result (quotient) in qH and qL. ; Upon return from a successful division, w contains 0. org 8 topH ds 1 ; MSB of top of fraction. topL ds 1 ; LSB of top of fraction. btmH ds 1 ; MSB of bottom of fraction. btmL ds 1 ; LSB of bottom of fraction. qH ds 1 ; MSB of quotient. qL ds 1 ; LSB of quotient. count ds 1 ; temporary counter index ds 1 ; temporary counter ; Device data and reset vector device pic16c55,xt_osc,wdt_off,protect_off reset start org 0 start mov !rb,#0 ; Set to outputs to display mov !rc,#0 ; results on LEDs. mov topH,#33h ; Problem: divide mov topL,#0Ah ; 330Ah by 00A3h. mov btmH,#0h mov btmL,#0A3h call divide mov rb,qL ; Display quotient in binary mov rc,qH ; on LEDs connected to rb and rc. jmp $ ; Endless loop Divide mov w,btmH ; Check for division by 0. OR w,btmL snz retw 255 ; Error code= 255: return. mov count,#1 ; Otherwise, initialize variables clr index ; for the division. clr qH clr qL :sh_loop jb btmH.7,:d1 ; Shift divisor left clc ; until msb is in rl btmL ; btmH.7. rl btmH ; count = no. of shifts+1. inc count jmp :sh_loop :d1 clc rl qL ; Shift quotient left. rl qH sub topL,btmL ; top = top - btm. jc :d2 ; If top - btm < 0 then sub topH,#1 ; top = top + btm jc :d2 ; The idea is to do the inc topH ; the subtraction and comparison add topL,btmL ; (top > btm?) in one step. goto :reentr ; Then, if btm > top, undo :d2 sub topH,btmH ; the subtraction by adding sc ; top and btm back together goto :less setb qL.0 :reentr clc rr btmH rr btmL djnz count,:d1 ret ; Return w/ remainder in top ; and result in q. :less ADD topL,btmL ; btm > top, so snc ; undo the subtraction by inc topH ; adding them back together. ADD topH,btmH goto :reentr
See also: